﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using netCoreStudy.Attr.QueryAttr;
using netCoreStudy.Enum;
using AspectCore.Extensions.Reflection;
using netCoreStudy.Entities.Base;
using static netCoreStudy.Express.ReflectorDic;

namespace netCoreStudy.Express
{
	public static class WhereExt
	{
		public static IQueryable<TSource> WhereExp<TSource, TP>(this IQueryable<TSource> queryList, TP parameter)
			where TP : BaseParameter
		{
			IQueryable<TSource> whereQuery = queryList.Where(WhereM<TSource, TP>(parameter));
			return whereQuery;
		}


		public static Expression<Func<TSource, bool>> GetWhereExp<TSource, T>(
			this T parameter) where T : BaseParameter
		{
			Expression<Func<TSource, bool>> exp = WhereM<TSource, T>(parameter);
			return exp;
		}

		/// <summary>
		/// 动态构造 where 表达式树 - 反射增强版本 
		/// </summary>
		/// <param name="searchOptionEntity"></param>
		/// <typeparam name="TSource"></typeparam>
		/// <typeparam name="TP"></typeparam>
		/// <returns></returns>
		public static Expression<Func<TSource, bool>> WhereM<TSource, TP>(
			TP searchOptionEntity)
		{
			Type typeSource = typeof(TSource); // 数据源列表元素对象类型(Student,People)
			Type typeSearch = searchOptionEntity.GetType();
			IEnumerable<PropertyReflector> sourceReflectors =
				GetPropReflector(typeSource);
			IEnumerable<PropertyReflector> searchReflectors =
				GetPropReflector(typeSearch);

			ParameterExpression p =
				Expression.Parameter(typeof(TSource), "p"); // p=>
			// 定义 一个 可叠加的 exp 树 (true=true)
			Expression parentExp =
				Expression.Equal(
					Expression.Constant(true),
					Expression.Constant(true));

			Expression tempExp =
				Expression.Equal(
					Expression.Constant(true),
					Expression.Constant(true));

			//遍历查询选择器对象的属性集合	
			foreach (PropertyReflector reflector in searchReflectors)
			{
				// 如果属性有 where 特性 
				WhereAttribute whereAttribute =
					reflector.GetCustomAttribute<WhereAttribute>();
				if (whereAttribute == null) continue;

				object propValue =
					reflector.GetValue(searchOptionEntity); // 获取 属性值 (name="haha")
				Type propertyPropertyType = reflector.GetPropertyInfo().PropertyType;
				// Console.WriteLine(
				// 	$"属性名:{whereAttribute.DisplayName}属性类型:{propertyPropertyType}属性值:{propValue}");
				if (propertyPropertyType == typeof(int) || propertyPropertyType == typeof(long) ||
				    propertyPropertyType.IsEnum)
				{
					if (propertyPropertyType == typeof(int))
					{
						// 当 int 或者 long  类型 没有传的时候 默认 是0 ,这个得 剔除掉……
						int intVal = (int) propValue;
						if (intVal == 0) continue;
					}

					if (propertyPropertyType == typeof(long))
					{
						long intVal = (long) propValue;
						if (intVal == 0) continue;
					}

					//对应的查询 parameter 枚举 从1 开始,不要从0 ，不然就把所有的都查到了……
					if (propertyPropertyType.IsEnum)
					{
						int intVal = (int) propValue;
						if (intVal == 0) continue;
					}
				}

				if (propValue == null) continue;


				string searchAttributeDisplayName =
					whereAttribute
						.DisplayName; //  自定义查询字段名称(属性) [search(displayName="name")]

				string searchAttributeBlurry =
					whereAttribute.Blurry; //  自定义查询字段名称(模糊字段 ) [search(blur="name,id")]
				//查询选择器中的该属性的自定义的对比查询的字段名称 in 数据源列表对象的属性集合 && 查询选择器中的该属性是查询的条件  &&  查询选择器该属性的值!=null或者""
				if (searchAttributeDisplayName != null)
				{
					tempExp = SinglePropExpGen(sourceReflectors,
						searchAttributeDisplayName, whereAttribute,
						propValue, p);
				}


				if (searchAttributeBlurry != null)
				{
					tempExp = BlurPropExpGen(sourceReflectors,
						searchAttributeBlurry, whereAttribute,
						propValue, p);
				}

				parentExp =
					Expression.AndAlso(parentExp, tempExp);
			}

			Console.WriteLine(parentExp);
			return Expression.Lambda<Func<TSource, bool>>(
				parentExp, p);
		}


		/// <summary>
		/// 单个属性表达式生成 
		/// </summary>
		/// <param name="sourceProperties"></param>
		/// <param name="searchAttributeDisplayName"></param>
		/// <param name="whereAttribute"></param>
		/// <param name="propValue"></param>
		/// <param name="p"></param>
		/// <returns></returns>
		/// <exception cref="ArgumentOutOfRangeException"></exception>
		private static Expression SinglePropExpGen(
			IEnumerable<PropertyReflector> sourceProperties,
			string searchAttributeDisplayName,
			WhereAttribute whereAttribute, object propValue, ParameterExpression p
		)
		{
			if (
				sourceProperties.Any(x => x.Name == searchAttributeDisplayName)
				&& whereAttribute.Query != QueryType.None
				&& propValue != null
				&& propValue.ToString() != string.Empty
			)
			{
				MemberExpression p_dot_prop = Expression.Property(p,
					searchAttributeDisplayName); // 类似 p.name ,p.id ,p.xxx

				Expression exp;
				ConstantExpression constantExpression =
					ConfirmValueType(sourceProperties,
						searchAttributeDisplayName,
						propValue);

				//根据查询选择器中该属性的查询条件进行不同的操作	
				//根据 枚举 获取 运算 符
				switch (whereAttribute.Query)
				{
					case QueryType.Equal:
						exp = Expression.Equal(p_dot_prop, constantExpression);
						break;
					case QueryType.GreaterThan:
						exp = Expression.GreaterThan(p_dot_prop, constantExpression);
						break;
					case QueryType.LessThan:
						exp = Expression.LessThan(p_dot_prop, constantExpression);
						break;
					case QueryType.GreaterThanOrEqual:
						exp = Expression.GreaterThanOrEqual(p_dot_prop, constantExpression);
						break;
					case QueryType.LessThanOrEqual:
						exp = Expression.LessThanOrEqual(p_dot_prop, constantExpression);
						break;
					default:
						throw new ArgumentOutOfRangeException();
				}

				// 累加条件 x=>x.id==1   --> x=>x.id==1 && x.name!="haha"
				return exp;
			}

			return null;
		}

		/// <summary>
		/// 模糊查询表达式生成 
		/// </summary>
		/// <param name="sourceProperties"></param>
		/// <param name="blurAttrName"></param>
		/// <param name="whereAttribute"></param>
		/// <param name="propValue"></param>
		/// <param name="p"></param>
		/// <returns></returns>
		/// <exception cref="ArgumentOutOfRangeException"></exception>
		private static Expression BlurPropExpGen(
			IEnumerable<PropertyReflector> sourceProperties,
			string blurAttrName,
			WhereAttribute whereAttribute, object propValue, ParameterExpression p)
		{
			// Expression tempExp = Expression.Equal(
			// 	Expression.Constant(true), Expression.Constant(true));
			string[]
				blurValues =
					blurAttrName.Split(','); // 拆分成 ["name","introduction"] 这样的数组 
			List<string> typeSourceNames =
				sourceProperties.Select(x => x.Name).ToList();
			foreach (string blurValue in blurValues)
			{
				if (typeSourceNames.All(x => blurValue != x))
				{
					throw new Exception(
						$"特性里Blurry的{blurValue}这个属性名有问题!" +
						$"\n类里面 没有 {blurValue} 这个属性! ");
				}
			}

			Expression tempExp = Expression.Constant(false);
			if (
				propValue != null
				&& propValue.ToString() != string.Empty
			)
			{
				propValue = propValue as string;
				propValue = propValue.ToString().ToLower();
				for (var i = 0; i < blurValues.Length; i++)
				{
					MemberExpression p_dot_prop = Expression.Property(p, blurValues[i]); // 类似 p.name ,p.id ,p.xxx

					// 确认 right 的 value 类型 
					// x => x.Name.ToLower().Contains(searchKey)	
					ConstantExpression constantExpression =
						ConfirmValueType(sourceProperties, blurValues[i],
							propValue);
					// contains 方法 

					// 小写方法 方法 
					MethodInfo methodInfoToLower = typeof(String).GetMethod("ToLower", new Type[] { });

					Expression exp_toLower = Expression.Call(p_dot_prop, methodInfoToLower);

					MethodInfo methodInfoContains =
						typeof(String).GetMethod("Contains", new Type[] {typeof(string)});
					ConstantExpression searchKey =
						Expression.Constant(propValue);

					MethodCallExpression exp =
						Expression.Call(exp_toLower,
							methodInfoContains, searchKey);

					// 累加条件 x=>x.id==1   --> x=>x.id==1 && x.name!="haha"
					if (i == 0)
					{
						tempExp = exp;
					}
					else
					{
						tempExp = Expression.OrElse(tempExp, exp);
					}
				}
			}

			return tempExp;
		}

		/// <summary>
		/// 确认 right 的 value 类型 
		/// </summary>
		/// <param name="sourceReflectors"></param>
		/// <param name="blurAttrName"></param>
		/// <param name="propValue"></param>
		/// <returns></returns>
		private static ConstantExpression ConfirmValueType(IEnumerable<PropertyReflector> sourceReflectors,
			string blurAttrName, object propValue)
		{
			// 获取数据源 列表 元素 对象 的单个属性 的 属性类型 
			// person 的 name 是 string 类型 ,age 是 int 类型 
			PropertyReflector reflector = sourceReflectors
				.FirstOrDefault(p => p.GetPropertyInfo().Name == blurAttrName);
			Type sourcePropType = reflector.GetPropertyInfo().PropertyType;
			bool isGenericType = sourcePropType.IsGenericType // 类型是不是泛型 
			                     && sourcePropType.GetGenericTypeDefinition() ==
			                     typeof(Nullable<>); // 检测 是不是可为空 

			ConstantExpression constantExpression = null;

			if (isGenericType) // 是否是可空类型 ,执行 值的转换 
			{
				object changeType =
					Convert.ChangeType(propValue,
						Nullable.GetUnderlyingType(sourcePropType));
				//如果可为空类型，则将propertySearch Value的类型设置为可为空类型	
				constantExpression =
					Expression.Constant(changeType,
						sourcePropType);
			}
			else // 不可空类型 
			{
				constantExpression =
					Expression.Constant(Convert.ChangeType(
						propValue,
						sourcePropType));
			}

			return constantExpression;
		}
	}
}